import java.io.File;
import java.net.InetAddress;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.HashMap;
import java.util.Map;

public class Client extends UnicastRemoteObject implements Client_itf {
	
	private static final long serialVersionUID = 5838449924074267286L;
	private static Map<Integer, SharedObject_itf> descripteurs;
	private static Server_itf server;
	private static Client_itf client;

	public Client() throws RemoteException {
		super();
	}


///////////////////////////////////////////////////
//         Interface to be used by applications
///////////////////////////////////////////////////

	// initialization of the client layer
	public static void init() {
			try {
				String URL = "//"+InetAddress.getLocalHost().getHostName()+":"+Registry.REGISTRY_PORT+"/server";
				server = (Server_itf)Naming.lookup(URL);
				descripteurs = new HashMap<Integer, SharedObject_itf>();
				client = new Client();
				System.out.println("Client initialized");
			} catch (RemoteException e) {
				System.out.println("Server must be launched");
				System.exit(1);
			} catch (Exception e) {
				e.printStackTrace();
			}
	}
	
	// lookup in the name server
	public static SharedObject lookup(String name) {
		int id = -1;
		SharedObject so = null;
		try {
			id = server.lookup(name);
			if(id==-1)
				return null;
			else {
				String className = server.getClassName(id);
				Class<?> stub = Class.forName(className+"_stub");
				so = (SharedObject)stub.newInstance();
				so.setId(id);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		descripteurs.put(id, so);
		return so;
	}
	
	// binding in the name server
	public static void register(String name, SharedObject_itf so) {
		try {
			server.register(name, ((SharedObject) so).getId());
			System.out.println(name+" registered in the server");
		} catch (RemoteException e) {
			e.printStackTrace();
		}
	}

	// creation of a shared object
	public static SharedObject create(Object o) {
		try {
			int id = server.create(o);
			String className = server.getClassName(id);
			if(! (new File(className+"_stub.java").exists())) {
				System.out.println("genrating");
				StubsManager.generateStub(className);
			}
			if(! (new File(className+"_stub.class").exists())) {
				System.out.println("compiling");
				
			}
			Class<?> stub = Class.forName(o.getClass().getName()+"_stub");
			SharedObject so = (SharedObject)stub.newInstance();
			so.setId(id);
			descripteurs.put(id, so);
			return so;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	
/////////////////////////////////////////////////////////////
//    Interface to be used by the consistency protocol
////////////////////////////////////////////////////////////

	// request a read lock from the server
	public synchronized static Object lock_read(int id) {
		try {
			return server.lock_read(id, client);
		} catch (RemoteException e) {
			e.printStackTrace();
		}
		return null;
	}

	// request a write lock from the server
	public synchronized static Object lock_write (int id) {
		try {
			return server.lock_write(id, client);
		} catch (RemoteException e) {
			e.printStackTrace();
		}
		return null;
	}

	// receive a lock reduction request from the server
	public Object reduce_lock(int id) throws java.rmi.RemoteException {
		SharedObject so = (SharedObject) descripteurs.get(id);
		return so.reduce_lock();
	}


	// receive a reader invalidation request from the server
	public void invalidate_reader(int id) throws java.rmi.RemoteException {
		SharedObject so = (SharedObject) descripteurs.get(id);
		so.invalidate_reader();
	}


	// receive a writer invalidation request from the server
	public Object invalidate_writer(int id) throws java.rmi.RemoteException {
		SharedObject so = (SharedObject) descripteurs.get(id);
		return so.invalidate_writer();
	}
	
	
	public static Map<Integer, SharedObject_itf> getDescripteurs() {
		return descripteurs;
	}
}